package org.mathig.poker.table;

import java.util.ArrayList;
import java.util.List;

import org.mathig.poker.PlayerWrapper;
import org.mathig.poker.StakesConfig;
import org.mathig.poker.util.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A table ok poker (10 players max).<br>
 * 
 * @author mathig
 */
public class Table {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private TableConfig config = TableConfig.DEFAULT;

	private Deck deck = new Deck();

	/**
	 * Player who has to bet Big Blind.<br>
	 * This is the reference because no player should skip BB.
	 * <p>
	 * Small Blind player is 1 seat before BB.<br>
	 * Button player is 2 seat before BB.
	 */
	private int bigBlindPlayer;

	private int handCount;

	private TableHand tableHand;

	/** Remaining table players (losers are removed). */
	private PlayerWrapper[] seats;

	private PlayerAmount stacks = new PlayerAmount();

	public Table(int seatCount) {
		this.seats = new PlayerWrapper[seatCount];
	}

	public void seat(List<PlayerWrapper> players) {
		int seatIndex = 0;
		for (PlayerWrapper player : players)
			seat(player, seatIndex++);
	}

	public void seat(PlayerWrapper player, int seatIndex) {
		if (seats[seatIndex] != null)
			throw new IllegalArgumentException(player + " cant seat at " + seatIndex + ", there is already someone !");

		seats[seatIndex] = player;
		stacks.set(player, config.getDefaultStack());

		logger.info("{} seat down to the table.", player);
	}

	public void leave(PlayerWrapper player) {
		for (int i = 0; i < seats.length; i++)
			if (seats[i] == player)
				seats[i] = null;
	}

	private void chooseFirstDealer() {
		// we don't save the dealer/button position but the BB
		bigBlindPlayer = RandomUtils.random.nextInt(getPlayers().size());
		logger.debug("{} is the first BB (choosed randomly)", getPlayers().get(bigBlindPlayer));

		int dealer = bigBlindPlayer - 2;
		if (dealer < 0)
			dealer += getPlayers().size();
		logger.info("{} is the first dealer (choosed randomly)", getPlayers().get(dealer));
	}

	public int getStack(PlayerWrapper player) {
		return stacks.get(player);
	}

	public PlayerAmount getStacks() {
		return stacks;
	}

	public List<PlayerWrapper> getPlayers() {
		List<PlayerWrapper> players2 = new ArrayList<PlayerWrapper>();
		for (PlayerWrapper player : this.seats) {
			if (player != null)
				players2.add(player);

		}
		return players2;
	}

	public void eliminate(PlayerWrapper player) {
		int index = getPlayers().indexOf(player);
		if (index <= bigBlindPlayer)
			bigBlindPlayer -= 1;
		leave(player);
		logger.info("{} is eliminated", player);
	}

	public Deck getDeck() {
		return deck;
	}

	/**
	 * Start the match.
	 */
	public PlayerWrapper play() {
		if (getPlayers().isEmpty())
			throw new IllegalStateException("can't play this table while there is NO player !");
		
		chooseFirstDealer();

		int ttl = 100; // TTL (Time To Live) to avoid infinite game
		while (getPlayers().size() > 1 && ttl-- > 0) {
			nextHand();
		}

		if (getPlayers().size() != 1)
			throw new RuntimeException("table ended but still players : " + getPlayers());

		PlayerWrapper winner = getPlayers().get(0);
		logger.info("{} wins after {} hands !!!", winner, handCount);
		logger.debug("{} stack = {}", winner, stacks.get(winner));
		return winner;
	}
	
	public String handString(){
		return "hand #" + handCount;
	}

	public void nextHand() {
		handCount++;
		logger.info(handString());

		// blind growing each X hands
		StakesConfig blindConfig = config.getBlinds(handCount / config.getHandCountBlindChange());
		logger.info("blinds : {}", blindConfig);

		List<PlayerWrapper> handPlayers = new ArrayList<PlayerWrapper>();
		int sbIndex = bigBlindPlayer - 1;
		if (sbIndex < 0)
			sbIndex += getPlayers().size();
		for (int i = sbIndex; i < getPlayers().size(); i++)
			handPlayers.add(getPlayers().get(i));
		for (int i = 0; i < sbIndex; i++)
			handPlayers.add(getPlayers().get(i));

		deck.shuffle();

		tableHand = new TableHand(this, handPlayers, blindConfig);
		tableHand.play();
	}

	public TableConfig getConfig() {
		return config;
	}

}
